package pl.edu.mimuw.irs.core.model.petrinet;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import pl.edu.mimuw.irs.core.exception.InternalException;
import pl.edu.mimuw.irs.core.exception.InternalException.Cause;
import pl.edu.mimuw.irs.core.model.petrinet.Arc.Direction;


/**
 * @author Jakub Rauch
 * Created on: 2009-05-07
 */
public class Net {
	//private static Logger logger = Logger.getLogger(Net.class);

	// *** Attributes
	
	private Map<String, Place> placesMap;
	private Map<String, Transition> transitionsMap;
	private Map<String, Arc> arcsMap;
	// Getters and setters
	private Map<String, Place> getPlacesMap() {
		return placesMap;
	}
	private void setPlacesMap(Map<String, Place> placesMap) {
		this.placesMap = placesMap;
	}
	private Map<String, Transition> getTransitionsMap() {
		return transitionsMap;
	}
	private void setTransitionsMap(Map<String, Transition> transitionsMap) {
		this.transitionsMap = transitionsMap;
	}
	private Map<String, Arc> getArcsMap() {
		return arcsMap;
	}
	private void setArcsMap(Map<String, Arc> arcsMap) {
		this.arcsMap = arcsMap;
	}
	public Collection<Place> getPlaces() {
		return placesMap.values(); // FIXME : Powinno zwracac kopie
	}
	public void addPlaces(Collection<Place> places) {
		Map<String, Place> placesMap = getPlacesMap();
		for (Place place : places) {
			placesMap.put(place.getId(), place);
			place.setNet(this);
		}
	}
	public void removePlace(String placeId) {
		Map<String, Place> placesMap = getPlacesMap();
		Place place = placesMap.get(placeId);
		place.setNet(null);
		placesMap.remove(placeId);
	}
	public void removePlaces(Set<String> placesIds) {
		for (String placeId : placesIds) {
			removePlace(placeId);
		}
	}
	public Collection<Transition> getTransitions() {
		return transitionsMap.values(); // FIXME : Powinno zwracac kopie
	}
	public void addTransitions(Collection<Transition> transitions) {
		Map<String, Transition> transitionsMap = getTransitionsMap();
		for (Transition transition : transitions) {
			transitionsMap.put(transition.getId(), transition);
			transition.setNet(this);
		}
	}
	private void removeTransition(String transitionId) {
		Map<String, Transition> transitionsMap = getTransitionsMap();
		Transition transition = transitionsMap.get(transitionId);
		transition.setNet(null);
		transitionsMap.remove(transitionId);
	}
	public void removeTransitions(Set<String> transitionsIds) {
		for (String transitionId : transitionsIds) {
			removeTransition(transitionId);
		}
	}
	public Collection<Arc> getArcs() {
		return arcsMap.values(); // FIXME : Powinno zwracac kopie
	}
	public void addArcs(Collection<Arc> arcs) {
		Map<String, Arc> arcsMap = getArcsMap();
		for (Arc arc : arcs) {
			arcsMap.put(arc.getId(), arc);
			arc.setNet(this);
		}
	}
	public void removeArc(String arcId) {
		Map<String, Arc> arcsMap = getArcsMap();
		Arc arc = arcsMap.get(arcId);
		arc.setNet(null);
		arcsMap.remove(arcId);
	}
	public void removeArcs(Set<String> arcsIds) {
		for (String arcId : arcsIds) {
			removeArc(arcId);
		}
	}
	public Place getPlace(String id) {
		return getPlacesMap().get(id);
	}
	public void addPlace(Place place) {
		place.setNet(this);
		getPlacesMap().put(place.getId(), place);
	}
	public Transition getTransition(String id) {
		return getTransitionsMap().get(id);
	}
	public void addTransition(Transition transition) {
		transition.setNet(this);
		getTransitionsMap().put(transition.getId(), transition);
	}
	public Arc getArc(String id) {
		return getArcsMap().get(id);
	}
	public void addArc(Arc arc) {
		arc.setNet(this);
		getArcsMap().put(arc.getId(), arc);
	}
	private void addArc(String placeId, String transitionId, Direction direction, int weight) {
		Transition transition = getTransitionsMap().get(transitionId);
		Place place = getPlacesMap().get(placeId);
		if (transition == null) throw new InternalException(Cause.PETRI_NET_UNDEFINED_ELEMENT, transitionId);
		if (place == null) throw new InternalException(Cause.PETRI_NET_UNDEFINED_ELEMENT, placeId);
		Arc arc = new Arc(place, transition, direction, weight);
		place.addArc(arc);
		transition.addArc(arc);
	}
	public void addPlaceTranArc(String placeId, String transitionId, int weight) {
		addArc(placeId, transitionId, Direction.PLACE_TRANSITION, weight);
	}
	public void addTranPlaceArc(String transitionId, String placeId, int weight) {
		addArc(placeId, transitionId, Direction.TRANSITION_PLACE, weight);
	}
	public void addPlaceTranArc(String placeId, String transitionId) {
		addPlaceTranArc(placeId, transitionId, 1);
	}
	public void addTranPlaceArc(String transitionId, String placeId) {
		addTranPlaceArc(transitionId, placeId, 1);
	}
	public void addArc(Place place, Transition transition, int weight) {
		addPlaceTranArc(place.getId(), transition.getId(), weight);
	}
	public void addArc(Place place, Transition transition) {
		addArc(place, transition, 1);
	}
	public void addArc(Transition transition, Place place, int weight) {
		addTranPlaceArc(transition.getId(), place.getId(), weight);
	}
	public void addArc(Transition transition, Place place) {
		addArc(transition, place, 1);
	}
	public void addNode(INode node) {
		if (node instanceof Place) {
			addPlace((Place) node);
		} else if (node instanceof Transition) {
			addTransition((Transition) node);
		} else {
			throw new InternalException(Cause.PETRI_NET_UNKNOWN_NODE_TYPE);
		}
	}
	public void removeNode(INode node) {
		if (node instanceof Place) {
			removePlace(((Place) node).getId());
		} else if (node instanceof Transition) {
			removeTransition(((Transition) node).getId());
		} else {
			throw new InternalException(Cause.PETRI_NET_UNKNOWN_NODE_TYPE);
		}
	}
	public Set<INode> getAllNodes() {
		Set<INode> allNodes = new LinkedHashSet<INode>();
		allNodes.addAll(getPlaces());
		allNodes.addAll(getTransitions());
		return allNodes;
	}
	
	// *** Methods
	
	public Net(Set<Place> places, Set<Transition> transitions, Set<Arc> arcs) {
		setPlacesMap(new HashMap<String, Place>());
		setTransitionsMap(new HashMap<String, Transition>());
		setArcsMap(new HashMap<String, Arc>());
		addPlaces(places);
		addTransitions(transitions);
		addArcs(arcs);
	}
	
	public Net() {
		this(new HashSet<Place>(), new HashSet<Transition>(), new HashSet<Arc>());
	}
	
	/**
	 * Searches whole graph to find all active transitions and returns them.
	 * @return
	 */
	public Set<Transition> getActiveTransitions() {
		Set <Transition> activeTransitions = new HashSet<Transition>();
		
		for (Transition transition : getTransitions()) {
			if (transition.isActive()) {
				activeTransitions.add(transition);
			}
		}
		
		return activeTransitions;
	}
	
	
}
